#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__docformat__ = "restructuredtext en"

import logging
import functools

S_INIT = 0x00
S_DEAL = 0x10
S_FILL = 0x20
S_RESTORE = 0x30
S_PLAY = 0x40
S_UNDO = 0x50
S_REDO = 0x60
S_FINISH = 0x90

def _find_game_class(cls):
    """
    Find game class either as attribute of object or self.

    .. \_find_game_class(cls:Object) -> Object
    """
    if hasattr(cls, 'game') and not cls.game is None:
        obj = cls.game
    else:
        obj = cls
    return obj


def move_state(attr='play'):
    """
    Set move state accordingly. Call decorated method. Set move state back.

    *note:*
      Object must be game subclass or have game attribute.

    .. @move_state([*game_move_state*])
    """

    def wrapper(func):

        @functools.wraps(func)
        def _change_state(cls, *args, **kwargs):
            """
            Set move state accordingly. Call decorated method. Set move state
            back.
            """
            obj = _find_game_class(cls)
            old_state = obj.move_state
            new_state = {'deal': S_DEAL,
                         'fill': S_FILL,
                         'restore': S_RESTORE,
                         'play': S_PLAY,
                         'undo': S_UNDO,
                         'redo': S_REDO,
                         'finish': S_FINISH}.get(attr.lower(), S_PLAY)
            # TODO: needs further verification
            if S_PLAY > new_state < old_state: # less play then less old
                obj.move_state = new_state
            elif S_PLAY < new_state > old_state: # more play then more old
                obj.move_state = new_state

            r_value = func(cls, *args, **kwargs)
            obj.move_state = old_state
            return r_value

        return _change_state

    return wrapper


def not_state(*attr):
    """
    Call decorated method only when given attribute is False.

    Checks mode of 'is_pause', is_demo, 'preview'.

    *note:*
      Object must be game subclass or have game attribute.

    .. @not_state([*game_mode_attribute*])
    """
    def wrapper(f):

        @functools.wraps(f)
        def _not_state(cls, *args, **kwargs):
            """
            Call decorated method only when given attribute is False.
            """
            obj = _find_game_class(cls)
            if not all(getattr(obj, mode) for mode in attr):
                return f(cls, *args, **kwargs)
        return _not_state
    return wrapper


def canvas_busy(f):
    """
    Set canvas busy state for decorated method.

    *note:*
      Object must be game subclass or have game attribute.

    .. @canvas_busy
    """

    @functools.wraps(f)
    def _canvas_busy(cls, *args, **kwargs):
        """
        Set canvas busy state for decorated method.
        """
        obj = _find_game_class(cls)
        if hasattr(obj, 'canvas'):
            busy = obj.canvas.busy
            obj.canvas.busy = True
            r = f(cls, *args, **kwargs)
            obj.canvas.busy = busy
            return r
        return f(cls, *args, **kwargs)
    return _canvas_busy


def game_busy(f):
    """
    Set game busy state for decorated method.

    *note:*
      Object must be game subclass or have game attribute.

    .. @game_busy
    """

    @functools.wraps(f)
    def _game_busy(cls, *args, **kwargs):
        """
        Set game busy attribute for decorated method.
        """
        obj = _find_game_class(cls)
        busy = obj.busy
        obj.busy = True
        results = f(cls, *args, **kwargs)
        obj.busy = busy
        return results

    return _game_busy


def cancel_drag(break_pause=True):
    """
    Stop current game events.

    Call stop methods for any active win animation, or demo. When given break
    pause, resume game. Call hide method for stack tips. Call cancel methods
    on active drag stack.

    .. @cancel_drag([break_pause:bool=True)
    """

    def wrapper(f):

        @functools.wraps(f)
        def check(cls, *args, **kwargs):
            """
            Stop current game events and return game to a normal state.
            """
            if hasattr(cls, 'game') and cls.game is not None:
                game = cls.game
                if game.win_animation is not None:
                    game.win_animation.stop()
                game.demo_stop()
                if break_pause:
                    game._timer_pause(False)  # Unpause game
                game._hide_stack_tip()
                if game.busy:
                    return
                if game.drag['stack']:
                    game.drag['stack'].cancelDrag()
            return f(cls, *args, **kwargs)

        return check

    return wrapper


def game_connect(f):
    """
    Confirm game is connected to object.

    .. @game_connect
    """

    @functools.wraps(f)
    def _game_connect(cls, *args, **kwargs):
        """
        Check object has a game object that is not None.
        """
        if hasattr(cls, 'game') and not cls.game is None:
            return f(cls, *args, **kwargs)

    return _game_connect